#include "TcpServer.hpp"
#include<memory>
using namespace std;
using namespace  tcpserver;

static void Usage(std::string proc)
{
    std::cerr<<"Usage:\n\t" <<proc <<" port"<<std::endl;
}

bool cal(const Request& req,Respone& resp)
{
    //我们已经有了结构化的数据了可以直接使用！
    resp.exitcode_ = 0;
    resp.result_ = 0;
    //初始一下

    switch(req.op_)
    {
        case '+':
            resp.result_ = req.x_ + req.y_;
            break;
        case '-':
            resp.result_ = req.x_ - req.y_;
            break;
        case '*':
            resp.result_ = req.x_ * req.y_;
            break;
        case '/':
        {
            if(req.y_ == 0)
            {
                resp.exitcode_ = DIV_ZERO;
                break;
            }
            resp.result_ = req.x_ / req.y_;
            break;
        }
        case '%':
        {
            if(req.y_ == 0)
            {
                resp.exitcode_ = MOD_ZERO;
                break;
            }
            resp.result_ = req.x_ % req.y_;
            break;
        }
        default:
            resp.exitcode_ = OP_ERR;
            break;
    }

    return true;
}


void Calculate(Connection* conn)
{
    std::string OnePackage;
    while(ParseOnePackage(conn->inbuffer_,&OnePackage))
    {
        std::cout << "in Calculate" << std::endl;
        std::string reqStr;
        if(!deLength(OnePackage,&reqStr))
            return;
        std::cout << "去掉报头的正文: \n"<<reqStr <<std::endl;

        Request req;
        if(!req.Deserialize(reqStr))//对报文进行反序列化
            return;

        Respone resp;
        cal(req,resp);//进行处理请求
        
        std::string respStr;
        resp.Serialize(&respStr);//对响应进行序列化！

        std::string SendStr = enLength(respStr);//添加报头！

        //发送回去！我们不能直接写入！我们要将其写事件放入epoll模型中！让其等待！因为用户无法保证发送缓冲区什么时候就绪！
        //写发送就绪的本质就是发送缓冲区有空间！
        //服务器刚开始启动时候，或者在很多情况下，发送事件一直是就绪的！
        //所以我们是可以直接发送了！只不过如果我们没有发送完该怎么办？
        //那么我们就需要下一次发送！等发送缓冲区有空间！
        //所以这里也要求每一个sock都要有自己的发送缓冲区！
        //下一次发的时候就要由epoll进行通知！所以要将sock的EPOLLOUT事件注册进epoll里面！
        //那么什么时候去注册呢？
        //一般读事件对于epoll来说我们是要常设的！
        //而写事件，其实发送的不频繁！所以对于epoll我们需要按需设置！
        //因为写事件是经常都是就绪的！这就会导致epoll一直返回！一直在就绪！
        //每一个文件描述符的缓冲区大概率是就绪的！所以epoll就一直在消耗CPU的资源！

        conn->outbuffer_+=SendStr;//把报文放到conn的发送缓冲区之中！
        //这样子就可以保存下来，防止没有发送出去！导致数据丢失！下一次还可以一起发送出去！

    }
    // 首先先来发送一次！
    if (conn->sender_)
        conn->sender_(conn); // 调用conn的发送函数

    // // 如果没有发送完毕！就需要对对应的sock开始对于写事件的关心！
    // // 如果发完了！我们就要关闭对于写事件的关心！
    // if(!conn->outbuffer_.empty())//不为空就是没有发送完毕
    // {
    //     conn->tsp_->EnableReadWrite(conn,true,true);
    //     // 因为调用这个接口需要用到TCP对象！但是我们没有！
    //     // 这时候回指指针就派上用场了！
    // }
    // else //发完了关闭
    // {
    //     conn->tsp_->EnableReadWrite(conn, true, false);
    // }
    //当然了最好的处理就是将这个写在sender函数里面！我们这里就是采用写在sender函数里面的写法！
}



int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<TcpServer> tsvr(new TcpServer(Calculate,port));
    tsvr->InitServer();
    tsvr->Dispatch();
}
//我们今天的Reactor服务器即负责就绪时间的通知也负责了业务处理！
//今天我们的Reactor服务器绑定的服务不是上面的Calculate
//如果是今天把一个报文构建了一个任务，然后扔到后端的线程池子里面！
//然后把connection对象传入，在线程池里面开启写事件关心然后把结果放进outbuffer里面！
//此时我们的Reactor就只负责事件就绪和IO不负责业务处理！
//还有一种很少见的模式叫做前摄式proactor
//如何将我们代码改成多线程呢？
//我们可以之间先创建多进程(多线程)！然后再每一个进程（线程）里面创建TcpServer对象
//将listensock放进其中一个进程（线程）的Reactor里面！
//一旦有链接就绪的时候，就要执行Accepter函数！
//那么此时就不止是要进行AddConnection了！而是要将其添加进哪一个进程(线程)epoll里面！
//listensock的进程(线程)就主要负责获取连接和分配任务到后端的多个线程的某个进程(线程)里面！


